/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simutanque;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.Date;

/**
 *
 * @author Augusto Wolf
 */
public class Tanque extends Thread {

    private static int tempo = 10;
    private static double velocidade;
    //<editor-fold defaultstate="collapsed" desc="Declaração de variáveis">
    private String nome;
    private double largura;
    private double altura;
    private double profundidade;
    private double volumeInicial;
    private double temperaturaInicial;
    private double diametroSaida;
    private double vazaoDeSaida;
    private double vazaoDeEntrada;
    private double periodo;
    private double amplitude;
    /**
     * 0 - Linear<br> 1 - Senoidal<br> 2 - Quadratica<br>
     */
    private int tipoFuncao;
    private double temperaturaDeEntrada;
    private double volumeAtual;
    private double temperaturaAtual;
    private double volumeMaximo;
    private double phInicial;
    private double phAtual;
    private double phDeEntrada;
    private boolean rodando;
    private boolean continuarRodando;
    private boolean pausar;
    private Date dataInicio = null;
    private Date dataFim = null;
    private long parado = 0;
    private Tanque anterior = null;
    private int n = 0;
    private TelaTanque tela = null;
    private final double GRAVIDADE = 9.8;
    private PainelComparaTanques painel = null;
    private long temp;//utilizado para calculo real do tempo decorrido
    private long tempoCalc;//utilizado para calculo real do tempo decorrido
    private boolean salvar;//utilizado para salvar dados da simulação
    private boolean avisouTransbordou = false;
    private File arq;
    private FileWriter writer;
    private PrintWriter saida;
    //</editor-fold>

    @Override
    public void run() {
        rodando = true;
        parado = 0;
        dataInicio = new Date();
        int i = 0;
        try {
            temp = new Date().getTime();
            while (continuarRodando) {
                if (pausar) {
                    long agora = new Date().getTime();
                    while (pausar) {
                        Thread.sleep(100);
                    }
                    parado += new Date().getTime() - agora;
                    temp = new Date().getTime();
                }
                if (salvar) {
                    adicionarDados();
                }
                calcular();
                Thread.sleep((long) (tempo / velocidade));
                if (i % 10 == 0) {
                    atualizarVisiveis();
                    i = 0;
                }
                i++;
            }
        } catch (Exception ex) {
        }
        dataFim = new Date();
        rodando = false;
    }

    @Override
    public String toString() {
        String s = "";
        s += nome;
        s += ";" + largura;
        s += ";" + profundidade;
        s += ";" + altura;
        s += ";" + volumeInicial;
        s += ";" + temperaturaInicial;
        s += ";" + phInicial;
        s += ";" + diametroSaida;
        if (anterior != null) {
            s += ";" + anterior.getNome();
        } else {
            s += ";null";
        }
        //s += ";" + vazaoDeEntrada;
        s += ";" + amplitude;
        s += ";" + periodo;
        s += ";" + tipoFuncao;
        s += ";" + temperaturaDeEntrada;
        s += ";" + phDeEntrada;
        return s;
    }

    private void calcular() {
        //System.out.println("Calcular  " + nome);
        temp = new Date().getTime() - temp;
        if (temp < tempo) {
            temp = tempo;
        }
        if (velocidade == 1) {
            tempoCalc = temp;
        } else {
            tempoCalc = tempo;
        }

        if (anterior != null) {
            this.vazaoDeEntrada = anterior.getVazaoDeSaida();
            this.temperaturaDeEntrada = anterior.getTemperaturaAtual();
            this.phDeEntrada = anterior.getPhAtual();
        } else {
            if (tipoFuncao == 0) {
                this.vazaoDeEntrada = amplitude;
            } else if (tipoFuncao == 1) {
                this.vazaoDeEntrada = amplitude * (1 + Math.sin(2 * Math.PI * getTempoSimulacao() / periodo)) / 2;
            } else if (tipoFuncao == 2) {
                this.vazaoDeEntrada = amplitude * (1 + Math.sin(2 * Math.PI * getTempoSimulacao() / periodo));
                if (vazaoDeEntrada < amplitude) {
                    vazaoDeEntrada = 0;
                } else {
                    vazaoDeEntrada = amplitude;
                }
            } else if (tipoFuncao == 3) {
                this.vazaoDeEntrada = Math.abs(amplitude * (Math.sin(2 * Math.PI * getTempoSimulacao() / periodo)));
            } else if (tipoFuncao == 4) {
                double aux = 0;
                for (int i = 0; i < 10; i++) {
                    aux += Math.sin((i * Math.PI) / 2) * ((Math.sin(i * periodo)) / Math.pow(i, 2));
                }
                this.vazaoDeEntrada = (8 / Math.pow(Math.PI, 2)) * aux;
            }
        }

        double areaBase = largura * profundidade;
        double areaSecaoSaida = Math.PI * Math.pow(diametroSaida / 2, 2);
        double alturaDeLiquido = volumeAtual / areaBase * 1000;
        double v = Math.sqrt(2 * GRAVIDADE * alturaDeLiquido);
        vazaoDeSaida = v * areaSecaoSaida;

        double volumeSaiu = vazaoDeSaida / (1000 / tempoCalc);
        double volumeEntrou = vazaoDeEntrada / (1000 / tempoCalc);

        /*
         * if (volumeAtual < 0) { volumeAtual = 0; } if (volumeEntrou < 0) {
         * volumeEntrou = 0; }
         */

        volumeAtual += -volumeSaiu;


        temperaturaAtual = (volumeAtual * temperaturaAtual + volumeEntrou * temperaturaDeEntrada) / (volumeAtual + volumeEntrou);

        phAtual = -Math.log10((volumeAtual * Math.pow(10, (-phAtual)) + volumeEntrou * Math.pow(10, (-phDeEntrada))) / (volumeAtual + volumeEntrou));

        volumeAtual += volumeEntrou;


        if (volumeAtual < 0) {
            volumeAtual = 0;
        } else if (volumeAtual > volumeMaximo) {
            volumeAtual = volumeMaximo;
            if (!avisouTransbordou) {
                avisouTransbordou = true;
                Thread t = new Thread() {

                    public void run() {
                        atualizarVisiveis();
                        Main.tp.pausar(true);
                        UTI.aviso(null, "O Tanque " + nome + " transbordou!\nVolume Atual = " + volumeAtual, "SimuTanque - Aviso");
                    }
                };
                t.start();
            }
        }

        if (temperaturaAtual < 0) {
            temperaturaAtual = 0;
        } else if (temperaturaAtual > 100) {
            temperaturaAtual = 100;
        }

        if (phAtual < 0) {
            phAtual = 0;
        } else if (phAtual > 14) {
            phAtual = 14;
        }
        temp = new Date().getTime();
        n++;
    }

    public Tanque(String nome, double largura, double profundidade, double altura, double volumeInicial, double temperaturaInicial, double phInicial, double diametroSaida) {
        if (temperaturaInicial < 0) {
            temperaturaInicial = 0;
        }
        if (temperaturaInicial > 100) {
            temperaturaInicial = 100;
        }
        if (phInicial < 0) {
            phInicial = 0;
        }
        if (phInicial > 14) {
            phInicial = 14;
        }
        this.nome = nome;
        this.largura = largura;
        this.profundidade = profundidade;
        this.altura = altura;
        this.volumeInicial = volumeInicial;
        this.temperaturaInicial = temperaturaInicial;
        this.diametroSaida = diametroSaida;
        this.volumeMaximo = largura * profundidade * altura * 1000;
        this.volumeAtual = volumeInicial;
        this.phInicial = phInicial;
        this.phAtual = phInicial;
        this.temperaturaAtual = temperaturaInicial;
    }

    public Tanque(String t) {

        String tt[] = t.split(";");

        nome = tt[0];
        largura = Double.parseDouble(tt[1]);
        profundidade = Double.parseDouble(tt[2]);
        altura = Double.parseDouble(tt[3]);
        volumeInicial = Double.parseDouble(tt[4]);
        temperaturaInicial = Double.parseDouble(tt[5]);
        phInicial = Double.parseDouble(tt[6]);
        diametroSaida = Double.parseDouble(tt[7]);
        if (!tt[8].equals("null")) {
            anterior = Gerencia.obterInstancia().getTanque(tt[8]);
        }

        //azaoDeEntrada = Double.parseDouble(tt[9]);
        amplitude = Double.parseDouble(tt[9]);
        periodo = Double.parseDouble(tt[10]);
        tipoFuncao = Integer.parseInt(tt[11]);

        temperaturaDeEntrada = Double.parseDouble(tt[12]);
        phDeEntrada = Double.parseDouble(tt[13]);

        if (temperaturaInicial < 0) {
            temperaturaInicial = 0;
        }
        if (temperaturaInicial > 100) {
            temperaturaInicial = 100;
        }

        if (phInicial < 0) {
            phInicial = 0;
        }
        if (phInicial > 14) {
            phInicial = 14;
        }

        this.volumeMaximo = largura * profundidade * altura * 1000;
        this.volumeAtual = volumeInicial;
        this.temperaturaAtual = temperaturaInicial;
    }

    public void exibirTela(boolean exibir) {
        if (tela == null) {
            this.tela = new TelaTanque(nome);
        }
        atualizarVisiveis();
        tela.setVisible(exibir);

    }

    public PainelComparaTanques getPainel() {
        if (painel == null) {
            this.painel = new PainelComparaTanques();
        }
        atualizarVisiveis();
        return painel;
    }

    private void adicionarDados() {
        String s = "";
        s += n;
        s += ";" + tempoCalc;
        s += ";" + UTI.formatoNumero.format(largura);
        s += ";" + UTI.formatoNumero.format(altura);
        s += ";" + UTI.formatoNumero.format(profundidade);
        s += ";" + UTI.formatoNumero.format(volumeInicial);
        s += ";" + UTI.formatoNumero.format(temperaturaInicial);
        s += ";" + UTI.formatoNumero.format(phInicial);
        s += ";" + UTI.formatoNumero.format(volumeMaximo);
        s += ";" + UTI.formatoNumero.format(diametroSaida);
        s += ";" + UTI.formatoNumero.format(vazaoDeEntrada);
        s += ";" + UTI.formatoNumero.format(temperaturaDeEntrada);
        s += ";" + UTI.formatoNumero.format(phDeEntrada);
        s += ";" + UTI.formatoNumero.format(volumeAtual);
        s += ";" + UTI.formatoNumero.format(temperaturaAtual);
        s += ";" + UTI.formatoNumero.format(phAtual);
        s += ";" + UTI.formatoNumero.format(vazaoDeSaida);
        saida.println(s);
    }

    public void atualizarVisiveis() {
        NumberFormat formatter = NumberFormat.getNumberInstance();
        if (painel != null) {
            painel.jProgressBar1.setMinimum(0);
            if (volumeMaximo < 10000) {
                painel.jProgressBar1.setMaximum((int) (volumeMaximo * 100) - 1);
                painel.jProgressBar1.setValue((int) (volumeAtual * 100));
            } else {
                painel.jProgressBar1.setMaximum((int) (volumeMaximo) - 1);
                painel.jProgressBar1.setValue((int) (volumeAtual));
            }

            painel.jlNome.setText(nome);
            painel.jlTempo.setText(getTempoSimulacao() + "");
            painel.jftfPhAtual.setText("" + formatter.format(phAtual));
            painel.jftfTemperaturaAtual.setText("" + formatter.format(temperaturaAtual));
            painel.jftfTemperaturaDeEntrada.setText("" + formatter.format(temperaturaDeEntrada));
            painel.jftfPhDeEntrada.setText("" + formatter.format(phDeEntrada));
            painel.jcbTipoFuncao.setSelectedIndex(getTipoFuncao());
            painel.jftfVazaoDeEntrada.setText("" + formatter.format(vazaoDeEntrada));
            painel.jftfVazaoDeSaida.setText("" + formatter.format(vazaoDeSaida));
            painel.jftfVolumeAtual.setText("" + formatter.format(volumeAtual));
            //painel.jftf.setText("" +);

            painel.jftfPhAtual.validate();
            painel.jftfPhDeEntrada.validate();
            painel.jftfTemperaturaAtual.validate();
            painel.jftfTemperaturaDeEntrada.validate();
            painel.jftfVazaoDeEntrada.validate();
            painel.jftfVazaoDeSaida.validate();
            painel.jftfVolumeAtual.validate();
        }
        if (tela != null) {
            tela.atualizar();
        }
    }

    //<editor-fold defaultstate="collapsed" desc="Condições que não podem mudar durante a simulação">
    public boolean setAltura(double altura) {
        if (rodando) {
            return false;
        }
        this.altura = altura;
        this.volumeMaximo = largura * profundidade * altura * 1000;
        return true;
    }

    public boolean setDiametroSaida(double diametroSaida) {
        if (rodando) {
            return false;
        }
        this.diametroSaida = diametroSaida;
        return true;
    }

    public boolean setLargura(double largura) {
        if (rodando) {
            return false;
        }
        this.largura = largura;
        this.volumeMaximo = largura * profundidade * altura * 1000;
        return true;
    }

    public boolean setProfundidade(double profundidade) {
        if (rodando) {
            return false;
        }
        this.profundidade = profundidade;
        this.volumeMaximo = largura * profundidade * altura * 1000;
        return true;
    }

    public boolean setTemperaturaInicial(double temperaturaInicial) {
        if (rodando) {
            return false;
        }
        this.temperaturaInicial = temperaturaInicial;
        return true;
    }

    public boolean setPhInicial(double phInicial) {
        if (rodando) {
            return false;
        }
        this.phInicial = phInicial;
        return true;
    }

    public boolean setVolumeInicial(double volumeInicial) {
        if (rodando) {
            return false;
        }
        this.volumeInicial = volumeInicial;
        return true;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Condições que podem mudar durante a simulação">
    public boolean setAnterior(Tanque anterior) {
        this.anterior = anterior;
        return true;
    }

    public boolean setTemperaturaDeEntrada(double temperaturaDeEntrada) {
        if (anterior != null) {
            return false;
        }
        if (temperaturaDeEntrada < 0) {
            temperaturaDeEntrada = 0;
        }
        if (temperaturaDeEntrada > 100) {
            temperaturaDeEntrada = 100;
        }
        this.temperaturaDeEntrada = temperaturaDeEntrada;
        return true;
    }

    public boolean setAmplitude(double amplitude) {
        if (anterior != null) {
            return false;
        }
        this.amplitude = amplitude;
        return true;
    }

    public boolean setPeriodo(double periodo) {
        if (anterior != null) {
            return false;
        }
        this.periodo = periodo;
        return true;
    }

    public boolean setTipoFuncao(int tipoFuncao) {
        if (anterior != null) {
            return false;
        }
        this.tipoFuncao = tipoFuncao;
        return true;
    }

    public boolean setPhDeEntrada(double phDeEntrada) {
        if (anterior != null) {
            return false;
        }
        this.phDeEntrada = phDeEntrada;
        return true;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Obter informações">
    public String getNome() {
        return nome;
    }

    public double getVolumeMaximo() {
        return volumeMaximo;
    }

    public double getPhAtual() {
        return phAtual;
    }

    public double getPhDeEntrada() {
        return phDeEntrada;
    }

    public double getPhInicial() {
        return phInicial;
    }

    public Tanque getAnterior() {
        return anterior;
    }

    public double getAltura() {
        return altura;
    }

    public double getDiametroSaida() {
        return diametroSaida;
    }

    public double getLargura() {
        return largura;
    }

    public double getProfundidade() {
        return profundidade;
    }

    public double getTemperaturaAtual() {
        return temperaturaAtual;
    }

    public double getTemperaturaDeEntrada() {
        return temperaturaDeEntrada;
    }

    public double getTemperaturaInicial() {
        return temperaturaInicial;
    }

    public double getVazaoDeEntrada() {
        return vazaoDeEntrada;
    }

    public double getVazaoDeSaida() {
        return vazaoDeSaida;
    }

    public double getVolumeAtual() {
        return volumeAtual;
    }

    public double getAmplitude() {
        return amplitude;
    }

    public double getPeriodo() {
        return periodo;
    }

    public int getTipoFuncao() {
        if (anterior == null) {
            return tipoFuncao;
        } else {
            return 5;
        }
    }

    public double getVolumeInicial() {
        return volumeInicial;
    }
//</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Condições da simulação">
    public void pausar(boolean pausar) {
        this.pausar = pausar;
    }

    public void parar() throws IOException {
        if (tela != null) {
            tela.dispose();
        }
        continuarRodando = false;
        if (salvar) {
            saida.close();
            writer.close();
        }
    }

    public void iniciar() {
        continuarRodando = true;
        pausar = false;
        this.start();
    }

    public boolean isPausar() {
        return pausar;
    }

    public boolean isRodando() {
        return rodando;
    }

    public Date getDataFim() {
        return dataFim;
    }

    public Date getDataInicio() {
        return dataInicio;
    }

    public long getParado() {
        return parado;
    }

    public long getTempoSimulacao() {
        if (velocidade == 1) {
            try {
                if (dataFim != null) {
                    return dataFim.getTime() - dataInicio.getTime();
                } else {
                    return new Date().getTime() - dataInicio.getTime() - parado;
                }
            } catch (Exception ex) {
                return 0;
            }
        } else {
            return (long) (n * 10);
        }
    }

    public void proximo() {
        calcular();
    }

    public void setSalvar(boolean salvar, File arq) throws IOException {
        this.salvar = salvar;
        if (salvar == true) {
            String s = "n";
            s += ";tempoCalculo";
            s += ";largura";
            s += ";altura";
            s += ";profundidade";
            s += ";volumeInicial";
            s += ";temperaturaInicial";
            s += ";phInicial";
            s += ";volumeMaximo";
            s += ";diametroSaida";
            s += ";vazaoDeEntrada";
            s += ";temperaturaDeEntrada";
            s += ";phDeEntrada";
            s += ";volumeAtual";
            s += ";temperaturaAtual";
            s += ";phAtual";
            s += ";vazaoDeSaida\n";
            UTI.gravarArquivo(arq, s);
            this.arq = arq;
            writer = new FileWriter(arq, true);
            saida = new PrintWriter(writer, true);
        } else {
            this.arq = null;
            writer = null;
            saida = null;
        }
    }

    public boolean isSalvar() {
        return salvar;
    }

    public int getN() {
        return n;
    }
    //</editor-fold>

    public static boolean setVelocidade(double velocidade) {
        Tanque.velocidade = velocidade;
        return true;
    }

    public static boolean setTempo(int tempo) {
        Tanque.tempo = tempo;
        return true;
    }
}
